import {
  assign,
  isArray,
  every
} from 'min-dash'

import {
  is
} from 'bpmn-js/lib/util/ModelUtil'

import {
  hasPrimaryModifier
} from 'diagram-js/lib/util/Mouse'

/**
   * A provider for BPMN 2.0 elements context pad
   */
export default function ContextPadProvider(
  config, injector, eventBus,
  contextPad, modeling, elementFactory,
  connect, create, popupMenu,
  canvas, rules, translate) {
  config = config || {}

  contextPad.registerProvider(this)

  this._contextPad = contextPad

  this._modeling = modeling

  this._elementFactory = elementFactory
  this._connect = connect
  this._create = create
  this._popupMenu = popupMenu
  this._canvas = canvas
  this._rules = rules
  this._translate = translate

  if (config.autoPlace !== false) {
    this._autoPlace = injector.get('autoPlace', false)
  }

  eventBus.on('create.end', 250, function(event) {
    // eslint-disable-next-line one-var
    var context = event.context,
      shape = context.shape

    if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) {
      return
    }

    var entries = contextPad.getEntries(shape)

    if (entries.replace) {
      entries.replace.action.click(event, shape)
    }
  })
}

ContextPadProvider.$inject = [
  'config.contextPad',
  'injector',
  'eventBus',
  'contextPad',
  'modeling',
  'elementFactory',
  'connect',
  'create',
  'popupMenu',
  'canvas',
  'rules',
  'translate'
]

ContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) {
  var modeling = this._modeling

  var actions = {}

  if (this._isDeleteAllowed(elements)) {
    assign(actions, {
      'delete': {
        group: 'edit',
        className: 'bpmn-icon-trash',
        title: this._translate('Remove'),
        action: {
          click: function(event, elements) {
            modeling.removeElements(elements.slice())
          }
        }
      }
    })
  }

  return actions
}

/**
   * @param {djs.model.Base[]} elements
   * @return {boolean}
   */
ContextPadProvider.prototype._isDeleteAllowed = function(elements) {
  var baseAllowed = this._rules.allowed('elements.delete', {
    elements: elements
  })

  if (isArray(baseAllowed)) {
    return every(baseAllowed, function(element) {
      return includes(baseAllowed, element)
    })
  }

  return baseAllowed
}

ContextPadProvider.prototype.getContextPadEntries = function(element) {
  // eslint-disable-next-line one-var
  var modeling = this._modeling,

    elementFactory = this._elementFactory,
    connect = this._connect,
    create = this._create,
    rules = this._rules,
    autoPlace = this._autoPlace,
    translate = this._translate

  var actions = {}

  if (element.type === 'label') {
    return actions
  }

  var businessObject = element.businessObject

  function startConnect(event, element) {
    connect.start(event, element)
  }

  function removeElement(e, element) {
    modeling.removeElements([element])
  }

  /**
     * Create an append action
     *
     * @param {string} type
     * @param {string} className
     * @param {string} [title]
     * @param {Object} [options]
     *
     * @return {Object} descriptor
     */
  function appendAction(type, className, title, options) {
    if (typeof title !== 'string') {
      options = title
      title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') })
    }

    function appendStart(event, element) {
      var shape = elementFactory.createShape(assign({ type: type }, options))
      create.start(event, shape, {
        source: element
      })
    }

    var append = autoPlace ? function(event, element) {
      var shape = elementFactory.createShape(assign({ type: type }, options))

      autoPlace.append(element, shape)
    } : appendStart

    return {
      group: 'model',
      className: className,
      title: title,
      action: {
        dragstart: appendStart,
        click: append
      }
    }
  }

  if (is(businessObject, 'bpmn:StartEvent')) {
    assign(actions, {
      'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none'),
      'append.user-task': appendAction('bpmn:UserTask', 'bpmn-icon-user-task'),
      'connect': {
        group: 'connect',
        className: 'bpmn-icon-connection-multi',
        title: translate('Connect using Association'),
        action: {
          click: startConnect,
          dragstart: startConnect
        }
      },
      'append.parallel-gateway': appendAction(
        'bpmn:ParallelGateway',
        'bpmn-icon-gateway-none',
        translate('Append ParallelGateway')
      ),
      'append.exclusive-gateway': appendAction(
        'bpmn:ExclusiveGateway',
        'bpmn-icon-gateway-xor',
        translate('Append ExclusiveGateway')
      ) 
    })
  }
  if (is(businessObject, 'bpmn:UserTask')) {
    assign(actions, {
      'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none'),
      'append.user-task': appendAction('bpmn:UserTask', 'bpmn-icon-user-task'),
      'connect': {
        group: 'connect',
        className: 'bpmn-icon-connection-multi',
        title: translate('Connect using Association'),
        action: {
          click: startConnect,
          dragstart: startConnect
        }
      },
      'append.parallel-gateway': appendAction(
        'bpmn:ParallelGateway',
        'bpmn-icon-gateway-none',
        translate('Append ParallelGateway')
      ),
      'append.exclusive-gateway': appendAction(
        'bpmn:ExclusiveGateway',
        'bpmn-icon-gateway-xor',
        translate('Append ExclusiveGateway')
      )
    })
  }
  if (is(businessObject, 'bpmn:ExclusiveGateway')) {
    assign(actions, {
      'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none'),
      'append.user-task': appendAction('bpmn:UserTask', 'bpmn-icon-user-task'),
      'append.parallel-gateway': appendAction(
        'bpmn:ParallelGateway',
        'bpmn-icon-gateway-none',
        translate('Append ParallelGateway')
      ),
      'append.exclusive-gateway': appendAction(
        'bpmn:ExclusiveGateway',
        'bpmn-icon-gateway-xor',
        translate('Append ExclusiveGateway')
      ),
      'connect': {
        group: 'connect',
        className: 'bpmn-icon-connection-multi',
        title: translate('Connect using Association'),
        action: {
          click: startConnect,
          dragstart: startConnect
        }
      }
    })
  }
  if (is(businessObject, 'bpmn:ParallelGateway')) {
    assign(actions, {
      'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none'),
      'append.user-task': appendAction('bpmn:UserTask', 'bpmn-icon-user-task'),
      'append.parallel-gateway': appendAction(
        'bpmn:ParallelGateway',
        'bpmn-icon-gateway-none',
        translate('Append ParallelGateway')
      ),
      'append.exclusive-gateway': appendAction(
        'bpmn:ExclusiveGateway',
        'bpmn-icon-gateway-xor',
        translate('Append ExclusiveGateway')
      ),
      'connect': {
        group: 'connect',
        className: 'bpmn-icon-connection-multi',
        title: translate('Connect using Association'),
        action: {
          click: startConnect,
          dragstart: startConnect
        }
      }
    })
  }
  // delete element entry, only show if allowed by rules
  var deleteAllowed = rules.allowed('elements.delete', { elements: [element] })

  if (isArray(deleteAllowed)) {
    // was the element returned as a deletion candidate?
    deleteAllowed = deleteAllowed[0] === element
  }

  if (deleteAllowed) {
    assign(actions, {
      'delete': {
        group: 'edit',
        className: 'bpmn-icon-trash',
        title: translate('Remove'),
        action: {
          click: removeElement
        }
      }
    })
  }

  return actions
}
function includes(array, item) {
  return array.indexOf(item) !== -1
}
